all repos — caroster @ v5.3

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Tooltip from '@mui/material/Tooltip';
  3import IconButton from '@mui/material/IconButton';
  4import Box from '@mui/material/Box';
  5import Link from '@mui/material/Link';
  6import Card from '@mui/material/Card';
  7import Container from '@mui/material/Container';
  8import TextField from '@mui/material/TextField';
  9import Typography from '@mui/material/Typography';
 10import TuneIcon from '@mui/icons-material/Tune';
 11import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 12import {useTheme} from '@mui/material/styles';
 13import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 14import {PropsWithChildren, useState} from 'react';
 15import {useTranslation} from 'react-i18next';
 16import pageUtils from '../../../lib/pageUtils';
 17import ShareEvent from '../../../containers/ShareEvent';
 18import PlaceInput from '../../../containers/PlaceInput';
 19import LangSelector from '../../../components/LangSelector';
 20import usePermissions from '../../../hooks/usePermissions';
 21import useEventStore from '../../../stores/useEventStore';
 22import useToastStore from '../../../stores/useToastStore';
 23import EventLayout, {TabComponent} from '../../../layouts/Event';
 24import {
 25  EventByUuidDocument,
 26  useUpdateEventMutation,
 27} from '../../../generated/graphql';
 28
 29interface Props {
 30  eventUUID: string;
 31  announcement?: string;
 32}
 33
 34const Page = (props: PropsWithChildren<Props>) => {
 35  return <EventLayout {...props} Tab={DetailsTab} />;
 36};
 37
 38const DetailsTab: TabComponent<Props> = ({}) => {
 39  const {t} = useTranslation();
 40  const {
 41    userPermissions: {canEditEventDetails},
 42  } = usePermissions();
 43  const theme = useTheme();
 44  const [updateEvent] = useUpdateEventMutation();
 45  const addToast = useToastStore(s => s.addToast);
 46  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 47  const event = useEventStore(s => s.event);
 48  const [isEditing, setIsEditing] = useState(false);
 49
 50  if (!event) return null;
 51
 52  const onSave = async e => {
 53    try {
 54      const {uuid, ...data} = event;
 55      const {
 56        id,
 57        travels,
 58        waitingPassengers,
 59        __typename,
 60        administrators,
 61        passengers,
 62        ...input
 63      } = data;
 64      await updateEvent({
 65        variables: {
 66          uuid,
 67          eventUpdate: {
 68            ...input,
 69          },
 70        },
 71        refetchQueries: ['eventByUUID'],
 72      });
 73      setIsEditing(false);
 74    } catch (error) {
 75      console.error(error);
 76      addToast(t('event.errors.cant_update'));
 77    }
 78  };
 79
 80  const modifyButton = isEditing ? (
 81    <Tooltip
 82      title={t('event.details.save')}
 83      sx={{
 84        position: 'absolute',
 85        top: theme.spacing(2),
 86        right: theme.spacing(2),
 87      }}
 88    >
 89      <IconButton color="primary" onClick={onSave}>
 90        <CheckCircleOutlineIcon />
 91      </IconButton>
 92    </Tooltip>
 93  ) : (
 94    <Tooltip
 95      title={t('event.details.modify')}
 96      sx={{
 97        position: 'absolute',
 98        top: theme.spacing(2),
 99        right: theme.spacing(2),
100      }}
101    >
102      <IconButton color="primary" onClick={() => setIsEditing(true)}>
103        <TuneIcon />
104      </IconButton>
105    </Tooltip>
106  );
107
108  return (
109    <Box
110      sx={{
111        position: 'relative',
112      }}
113    >
114      <Container
115        sx={{
116          p: 4,
117          mt: 6,
118          mb: 11,
119          mx: 0,
120          [theme.breakpoints.down('md')]: {
121            p: 2,
122          },
123        }}
124      >
125        <Card
126          sx={{
127            position: 'relative',
128            maxWidth: '100%',
129            width: '480px',
130            p: 2,
131          }}
132        >
133          <Typography variant="h4" pb={2}>
134            {t('event.details')}
135          </Typography>
136          {canEditEventDetails() && modifyButton}
137          {(isEditing || event.name) && <Box pt={2} pr={1.5}>
138            <Typography variant="overline">{t('event.fields.name')}</Typography>
139            <Typography>
140              {isEditing ? (
141                <TextField
142                  size="small"
143                  fullWidth
144                  value={event.name}
145                  onChange={e => setEventUpdate({name: e.target.value})}
146                  name="name"
147                  id="EditEventName"
148                />
149              ) : (
150                <Typography id="EventName">
151                  {event.name}
152                </Typography>
153              )}
154            </Typography>
155          </Box>}
156          {(isEditing || event.address) && <Box pt={2} pr={1.5}>
157            <Typography variant="overline">{t('event.fields.date')}</Typography>
158            {isEditing ? (
159              <Typography>
160                <DatePicker
161                  slotProps={{
162                    textField: {
163                      size: 'small',
164                      id: `EditEventDate`,
165                      fullWidth: true,
166                      placeholder: t('event.fields.date_placeholder'),
167                    },
168                  }}
169                  format="DD/MM/YYYY"
170                  value={moment(event.date)}
171                  onChange={date =>
172                    setEventUpdate({
173                      date: !date ? null : moment(date).format('YYYY-MM-DD'),
174                    })
175                  }
176                />
177              </Typography>
178            ) : (
179              <Box position="relative">
180                <Typography id="EventDate">
181                   {moment(event.date).format('DD/MM/YYYY')}
182                </Typography>
183              </Box>
184            )}
185          </Box>}
186          {(isEditing || event.address) && (
187            <Box pt={2} pr={1.5}>
188              <Typography variant="overline">
189                {t('event.fields.address')}
190              </Typography>
191              {isEditing ? (
192                <PlaceInput
193                  place={event.address}
194                  latitude={event.latitude}
195                  longitude={event.longitude}
196                  onSelect={({place, latitude, longitude}) =>
197                    setEventUpdate({
198                      address: place,
199                      latitude,
200                      longitude,
201                    })
202                  }
203                />
204              ) : (
205                <Box position="relative">
206                  <Typography id="EventAddress" sx={{pr: 3}}>
207                    <Link
208                      target="_blank"
209                      rel="noreferrer"
210                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
211                        event.address
212                      )}`}
213                      onClick={e => e.preventDefault}
214                    >
215                      {event.address}
216                    </Link>
217                  </Typography>
218                </Box>
219              )}
220            </Box>
221          )}
222          {(isEditing || event.description) && (
223            <Box pt={2} pr={1.5}>
224              <Typography variant="overline">
225                {t('event.fields.description')}
226              </Typography>
227              {isEditing ? (
228                <Typography>
229                  <TextField
230                    fullWidth
231                    multiline
232                    maxRows={4}
233                    inputProps={{maxLength: 250}}
234                    value={event.description || ''}
235                    onChange={e =>
236                      setEventUpdate({description: e.target.value})
237                    }
238                    id={`EditEventDescription`}
239                    name="description"
240                  />
241                </Typography>
242              ) : (
243                <Typography id="EventDescription" sx={{pr: 3}}>
244                  {event.description}
245                </Typography>
246              )}
247            </Box>
248          )}
249          {(isEditing || event.lang) && (
250            <Box pt={2} pr={1.5}>
251              <Typography variant="overline">
252                {t('event.fields.lang')}
253              </Typography>
254              {isEditing ? (
255                <LangSelector
256                  value={event.lang}
257                  onChange={lang => setEventUpdate({lang})}
258                />
259              ) : (
260                <Typography id="EventLang" sx={{pr: 3}}>
261                  {t(`PROTECTED.languages.${event.lang}`)}
262                </Typography>
263              )}
264            </Box>
265          )}
266          {!isEditing && (
267            <ShareEvent
268              title={`Caroster ${event.name}`}
269              sx={{width: '100%', mt: 2}}
270            />
271          )}
272        </Card>
273      </Container>
274    </Box>
275  );
276};
277
278export const getServerSideProps = pageUtils.getServerSideProps(
279  async (context, apolloClient) => {
280    const {uuid} = context.query;
281    const {host = ''} = context.req.headers;
282    let event = null;
283
284    // Fetch event
285    try {
286      const {data} = await apolloClient.query({
287        query: EventByUuidDocument,
288        variables: {uuid},
289      });
290      event = data?.eventByUUID?.data;
291    } catch (error) {
292      return {
293        notFound: true,
294      };
295    }
296
297    return {
298      props: {
299        eventUUID: uuid,
300        metas: {
301          title: event?.attributes?.name || '',
302          url: `https://${host}${context.resolvedUrl}`,
303        },
304      },
305    };
306  }
307);
308export default Page;